import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Iterator;
import java.util.Scanner;

import javax.swing.JPanel;

/**
 *  Container for all information about currently board.
 * @author mx109
 *
 */
public class Board extends JPanel implements Cloneable, Iterable<Field>{


	private static final long serialVersionUID = -708610072982715990L;
	private Field tab[][];
	public int rozx, rozy,players,boxes;
	private Player pl[];
	private Box bo[];
	public History history=new History(this);
	
	@SuppressWarnings("unused")
	private Board(){}
	
	/**
	 * Load new board from file.
	 * @param f file from which board is being loaded
	 * @param i number of the way the board was saved (actually there is only one way, and have number 1)
	 */
	public Board(File f,int i){
		
		this.setBackground(Color.DARK_GRAY);
		this.setAlignmentX(CENTER_ALIGNMENT);
		this.setAlignmentY(CENTER_ALIGNMENT);
		
		Player.setZero();
		Box.setZero();
		if (i==1) Constr(f);
	}
	/**
     * Copy constructor.
     *
     * @author crezax
     */
	public Board(Board b){
		rozx = b.rozx;
		rozy = b.rozy;
		players = b.players;
		boxes = b.boxes;
		pl = new Player[b.pl.length];
		bo = new Box[b.bo.length];
		tab = new Field[rozx][rozy];
		for (int i=0; i<rozx; i++)
			for (int j=0; j<rozy; j++){
				tab[i][j] = new Field(b.tab[i][j]);
			}
		for (int i=0; i<pl.length; i++){
			pl[i] = new Player(b.pl[i], this);
			tab[pl[i].getPosition().x][pl[i].getPosition().y].movObj = pl[i];
		}
		for (int i=0; i<bo.length; i++){
			bo[i] = new Box(b.bo[i], this);
			tab[bo[i].getPosition().x][bo[i].getPosition().y].movObj = bo[i];
		}
	}
	/**
	 * Prepares empty board (used by creating new board).
	 * @param width
	 * @param height
	 * @author wlis
	 */
	public Board(int width, int height)
	{
		this.setFocusable(false);
		tab = new Field[width][height];
		pl = new Player[0];
		bo = new Box[0];
		rozx = width;
		rozy = height;
		prepareLayout();
		for(int i = 0;i < rozy ; ++i)
		{
			for(int j = 0; j < rozx; ++j)
			{
				tab[j][i] = new Field();
				this.add(tab[j][i]);
			}
		}
		validate();
	}
	/**
     * A method, that removes all players and boxes from the board and puts new ones based on a given BoardState object.
     * Note, that this method should be used to reposition objects on a board and it may not work correctly if you use it with a BoardState object created from different board.
     *
     * @author crezax
     * @param boardState BoardState object containing modified positions of objects existing on the board.
     */
	public void modifyPositions(BoardState boardState) {
		Position pos[] = boardState.getPlayers();
		for (int i=0; i<pl.length; i++){
			tab[pl[i].getPosition().x][pl[i].getPosition().y].movObj = null;
			pl[i] = new Player(pos[i], this, false);
			tab[pos[i].x][pos[i].y].movObj = pl[i]; 
		}
		pos = boardState.getBoxes();
		for (int i=0; i<bo.length; i++){
			tab[bo[i].getPosition().x][bo[i].getPosition().y].movObj = null;
			bo[i] = new Box(pos[i], this);
			tab[pos[i].x][pos[i].y].movObj = bo[i]; 
		}
	}
	
	public boolean isFinished()
	{
		for(Box b : bo)
			if(check(b.getPosition()).type != Type.HOLE)return false;
		return true;
	}
	
	private void prepareLayout()
	{
		this.setLayout(new GridLayout(rozy, rozx, 1, 1));
		this.setPreferredSize(new Dimension(rozx*41-1, rozy*41-1));
		this.validate();
	}
	
	public void changeField(Position p, Field f){
		tab[p.x][p.y]=f;
	}
	
	/**
	 * 
	 * @return array of Players sorted by id
	 */
	public Player[] getPlayers(){return pl;}
	
	/**
	 * 
	 * @return array of Boxes sorted by id
	 */
	public Box[] getBoxes(){return bo;}
	
	/**
	 * 
	 * @param index id of chosen player
	 * @return references to a player with the given id
	 */
	public Player getPlayer(int index){return pl[index];}
	
	/**
	 * 
	 * @param index id of chosen pox
	 * @return references to a box with the given id
	 */
	public Box getBox(int index){return bo[index];}
	
	/**
	 * 
	 * @param p position of chosen field
	 * @return references to a field with the given position
	 */
	public Field check(Position p){return tab[p.x][p.y];}
	
	private void Constr(File f){
		try {
			Scanner sc = new Scanner(f);
			rozx=sc.nextInt(); 
			rozy=sc.nextInt();
			prepareLayout();
			tab=new Field[rozx][rozy];
			for (int j=0; j<rozy; j++)
			{
				for (int i=0; i<rozx; i++){
					int t; t=sc.nextInt();
					Field q= new Field();
					q.setPosition(new Position(i,j));
					if (t==0) q.type=Type.EMPTY;
					if (t==1) q.type=Type.HOLE;
					if (t==2) q.type=Type.WALL;
					tab[i][j]=q;
					this.add(q);
				}
			}
				
			players=sc.nextInt();
			pl=new Player[players];
			for (int i=0; i<players; i++){
				int x=sc.nextInt();
				int y=sc.nextInt();
				tab[x][y].movObj=pl[i]=new Player(new Position(x,y),this);
				
				}
				
			boxes=sc.nextInt();
			bo=new Box[boxes];
			for (int i=0; i<boxes; i++){
				int x=sc.nextInt(),y=sc.nextInt();
				tab[x][y].movObj=bo[i]=new Box(new Position(x,y),this);
			}
		} catch (FileNotFoundException e) {}
	}
	
	
	
	

	@Override
	public Iterator<Field> iterator() {
		
		return new iter(this);
	}
	
	private class iter implements Iterator<Field>{

		Board b;
		int f=0,g=0;
		public iter(Board d){this.b=d;}
		
		@Override
		public boolean hasNext() {
			if (f<b.rozx && g<b.rozy) return true;
			return false;
		}

		@Override
		public Field next() {
			Field ret=tab[f][g];
			f++;
			if (f>=b.rozx) {f=0; g++;}
			return ret;
		}

		@Override
		public void remove() {}
		
	}
	
	void addFieldsListeners()
	{
		for(Field[] row : tab)
		{
			for(Field f : row)
			{
				f.addMouseListener(new Field.FieldClickedListener(this, f));
			}
		}
	}
	
	@Override
	public void paintComponent(Graphics g)
	{
		super.paintComponent(g);
		this.paintBorder(g);
	}
		
	
}
